
#include <sys/epoll.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

#include <Dispatcher.h>
#include <EventLoop.h>

#define Max 520
struct EpollData {
    int epfd;
    struct epoll_event* evevts;
};

static void* epollInit();
static int epollAdd(struct Channel* channel, struct EventLoop* evLoop);
static int epollRemove(struct Channel* channel, struct EventLoop* evLoop);
static int epollModify(struct Channel* channel, struct EventLoop* evLoop);
static int epollDispatch(struct EventLoop* evLoop, int timeout);
static int epollClear(struct EventLoop* evLoop);
static int epollCtl(struct Channel* channel, struct EventLoop* evLoop, int op);

struct Dispatcher EpollDispatcher = {
    epollInit,
    epollAdd,
    epollRemove,
    epollModify,
    epollDispatch,
    epollClear
};

static void* epollInit() {
    struct EpollData* data = (struct EpollData*)malloc(sizeof (struct EpollData));
    data->epfd = epoll_create(10);
    if(data->epfd == -1) {
        perror("epoll_create");
        exit(0);
    }
    data->evevts = (struct epoll_event*)calloc(Max, sizeof (struct epoll_event));
    return data;
}

static int epollCtl(struct Channel* channel, struct EventLoop* evLoop, int op) {
    struct EpollData *data = (struct EpollData*)evLoop->dispatcherData;
    struct epoll_event ev;
    ev.data.fd = channel->fd;
    int events = 0;

    if(channel->events & ReadEvent) {
        events |= EPOLLIN;
    }

    if(channel->events & WriteEvent) {
        events |= EPOLLOUT;
    }

    ev.events = events;
    int ret = epoll_ctl(data->epfd, op, channel->fd, &ev);
    return ret;
}

static int epollAdd(struct Channel* channel, struct EventLoop* evLoop) {
    int ret = epollCtl(channel, evLoop, EPOLL_CTL_ADD);
    if(-1 == ret){
        perror("epoll_ctl_add");
        exit(0);
    }
    return ret;
}

static int epollRemove(struct Channel* channel, struct EventLoop* evLoop) {
    int ret = epollCtl(channel, evLoop, EPOLL_CTL_DEL);
    if(-1 == ret) {
        perror("epoll_ctl_del");
        exit(0);
    }
    channel->destroyCallback(channel->arg);
    return ret;
}

static int epollModify(struct Channel* channel, struct EventLoop* evLoop) {
    int ret = epollCtl(channel, evLoop, EPOLL_CTL_MOD);
    if(-1 == ret){
        perror("epoll_ctl_modfily");
        exit(0);
    }
    return ret;
}

static int epollDispatch(struct EventLoop* evLoop, int timeout) {
    struct EpollData* data = (struct EpollData*)evLoop->dispatcherData;
    int count = epoll_wait(data->epfd, data->evevts, Max, timeout * 1000);
    for(int i = 0; i < count; ++i) {
        int events = data->evevts[i].events;
        int fd = data->evevts[i].data.fd;
        if(events & EPOLLERR || events & EPOLLHUP) {
            continue;
        }

        if(events & EPOLLIN){
            eventActivate(evLoop, fd, ReadEvent);
        }

        if(events & EPOLLOUT) {
            eventActivate(evLoop, fd, WriteEvent);
        }
    }
    return 0;
}

static int epollClear(struct EventLoop* evLoop) {
    struct EpollData* data  =(struct EpollData*)evLoop->dispatcherData;
    free(data->evevts);
    close(data->epfd);
    free(data);
    return 0;
}



